#pragma once

#include <iostream>
#include <unistd.h>
#include <cstring> //C++版本的string.h
#include <cerrno>  //C++版本的errno.h
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fcntl.h>
#include <vector>
#include <ctime>
#include <cstdlib>
#include <cstdio>

// myfifo需要在使用的时候先在命令行中输入 mkfifo myfifo
// 之后创建的  NamedPipe类型的对象，默认围绕的都是这个管道文件进行操作
const std::string common_path = "./myfifo";
#define DefaultFd -1
#define Creator 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

class NamedPipe
{
private:
    // 进行封装之后，
    // 那么在使用的时候，可以先先创建一个管道文件
    // 然后让不同的NamedPipe的对象可以使用不同的方式访问这个管道
    // 此时已经有了管道文件，不同的进程想要以不同的方式访问这个管道，只需要指定读或写方式即可
    // 此时的设计是以不同方式打开，而且进程对应的fd_array[]数组的第_fd号文件对应的指针指向这个管道文件
    bool openNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
        {
            return false;
        }
        return true;
    }
    
public:
    // 管道对象在创建的时候调用构造函数NamedPipe()
    // 保证只有当进程是创建管道的对象时，才会执行创建逻辑
    NamedPipe(const std::string &path, int who)
        : _fifo_path(path)
        , _id(who)
        , _fd(DefaultFd)
    {
        if (_id == Creator)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            // 返回值不为零，创建失败
            if (res != 0)
            {
                perror("myfifo");
            }
            std::cout << "creator create named pipe" << std::endl;
        }
    }

    //用两个pubilc修饰的函数分别作为读 写方式打开文件的两个接口
    bool openForRead()
    {
        return openNamedPipe(Read);
    }

    bool openForWrite()
    {
        return openNamedPipe(Write);
    }

    int readNamedPipe(std::string *out)//这里之所以用指针，完全是因为想要表明这个参数是输出型参数
    {
        char buffer[BaseSize];
        // ssize_t read(int fd, void * buf, size_t count);
        // read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中
        // 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据
        int n = read(_fd, buffer, sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;        
    }

    int writeNamedPipe(const std::string &in)//这里之所以用const修饰的引用，完全是因为想要表明这个参数是输入型参数
    {
        // ssize_t write (int fd, const void * buf, size_t count); 
        // write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内
        // 如果顺利write()会返回实际写入的字节数（len）
        return write(_fd, in.c_str(), in.size());
    }

    ~NamedPipe()
    {
        if (_id == Creator)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creator free named pipe" << std::endl;
        }
        if (_fd != DefaultFd)
        {
            close(_fd);
        }
        
    }

private:
    const std::string _fifo_path;
    // _id是为了区分当前NamePipe类型的对象是为了
    int _id;
    int _fd;
};
